package org.jvm.classfile;

import org.jvm.classfile.attributeinfo.*;
import org.jvm.classfile.constantinfo.ConstantClassInfo;

/**
 * author : wangsixiang02
 * date : 2023/1/7
 * desc :class文件结构
 */
public class ClassFile {
    /**
     * 魔数，标识.class文件格式。应当为0xCAFEBABE
     */
    private int magic;
    /**
     * 副版本号
     */
    private int minorVersion;
    /**
     * 主版本号
     */
    private int majorVersion;
    /**
     * 常量池
     */
    private ConstantPool constantPool;
    /**
     * 访问标识，指出文件是类还是接口？访问权限如何？
     */
    private int accessFlags;
    /**
     * 本类索引，指向常量池中本类常量
     */
    private int thisClass;
    /**
     * 超类索引，指向常量池中本类的父类常量
     */
    private int superClass;
    /**
     * 接口索引，指向常量池中本类实现的所有接口常量
     */
    private int[] interfaces;
    /**
     * 本类所有类字段信息
     */
    private MemberInfo[] fields;
    /**
     * 本类所有方法信息
     */
    private MemberInfo[] methods;
    /**
     * 本类所有属性
     */
    private AttributeInfo[] attributes;

    public ClassFile(byte[] classData) {
        ClassReader classReader = new ClassReader(classData);
        read(classReader);
    }

    /**
     * 读取并解析字节码
     * 必须依次读取
     *
     * @param classReader
     */
    private void read(ClassReader classReader) {
        readAndCheckMagic(classReader);
        readAndCheckVersion(classReader);
        this.constantPool = readConstantPool(classReader);
        this.accessFlags = classReader.readUint16();
        this.thisClass = classReader.readUint16();
        this.superClass = classReader.readUint16();
        this.interfaces = classReader.readUint16s();
        this.fields = readMembers(classReader);
        this.methods = readMembers(classReader);
        this.attributes = readAttributes(classReader);
    }

    /**
     * 读取并检查魔法数是否符合要求
     *
     * @param classReader
     */
    private void readAndCheckMagic(ClassReader classReader) {
        this.magic = classReader.readUint32();
        if (this.magic != 0xCAFEBABE) {
            throw new RuntimeException("魔法数不符合要求");
        }
    }

    /**
     * 读取并检查版本号是否在本虚拟机的处理范围
     * 本虚拟机支持到java8，对应的主版本号为52
     *
     * @param classReader
     */
    private void readAndCheckVersion(ClassReader classReader) {
        this.minorVersion = classReader.readUint16();
        this.majorVersion = classReader.readUint16();

        if (this.majorVersion >= 45 && this.majorVersion <= 58) {
            return;
        }
        throw new RuntimeException("不支持class版本");
    }

    /**
     * 读取常量池
     *
     * @param classReader
     * @return
     */
    private ConstantPool readConstantPool(ClassReader classReader) {
        return new ConstantPool(classReader);
    }

    /**
     * 批量读取类成员，包含类字段和方法
     *
     * @param classReader
     * @return
     */
    private MemberInfo[] readMembers(ClassReader classReader) {
        //先读一个short确定成员数量
        int memberNum = classReader.readUint16();
        MemberInfo[] res = new MemberInfo[memberNum];
        for (int i = 0; i < memberNum; i++) {
            res[i] = readMember(classReader);
        }
        return res;
    }

    /**
     * 读取单个类成员
     *
     * @param classReader
     * @return
     */
    private MemberInfo readMember(ClassReader classReader) {
        int accessFlags = classReader.readUint16();
        int nameIndex = classReader.readUint16();
        int descriptorIndex = classReader.readUint16();
        AttributeInfo[] attributes = readAttributes(classReader);
        return new MemberInfo(this.constantPool, accessFlags, nameIndex, descriptorIndex, attributes);
    }


    /**
     * 读取属性表
     *
     * @param classReader
     * @return
     */
    private AttributeInfo[] readAttributes(ClassReader classReader) {
        return AttributeInfoBuilder.newAttributeInfos(classReader, this.constantPool);
    }


    public int getMinorVersion() {
        return minorVersion;
    }

    public ConstantPool getConstantPool() {
        return constantPool;
    }

    public void setConstantPool(ConstantPool constantPool) {
        this.constantPool = constantPool;
    }

    public int getAccessFlags() {
        return accessFlags;
    }

    public int getThisClass() {
        return thisClass;
    }

    public int getSuperClass() {
        return superClass;
    }


    public int[] getInterfaces() {
        return interfaces;
    }

    public void setInterfaces(int[] interfaces) {
        this.interfaces = interfaces;
    }

    public MemberInfo[] getFields() {
        return fields;
    }

    public void setFields(MemberInfo[] fields) {
        this.fields = fields;
    }

    public MemberInfo[] getMethods() {
        return methods;
    }

    public void setMethods(MemberInfo[] methods) {
        this.methods = methods;
    }

    public AttributeInfo[] getAttributes() {
        return attributes;
    }

    public void setAttributes(AttributeInfo[] attributes) {
        this.attributes = attributes;
    }

    public String getClassNameByIndex(int index) {
        if (index == 0) {
            return null;
        }
        ConstantClassInfo constantClassInfo = (ConstantClassInfo) this.constantPool.getConstantInfoByIndex(index);
        return constantClassInfo.name();
    }

    public String[] getInterfaceNames() {
        String[] interfaceNames = new String[this.interfaces.length];
        for (int i = 0; i < this.interfaces.length; i++) {
            interfaceNames[i] = this.constantPool.getUtf8(i);
        }
        return interfaceNames;
    }
}
